#include "StdAfx.h"

#include "Floor.h"
#include "FloorsManager.h"
#include "listeners.h"
#include "Passenger.h"

CFloorsManager::CFloorsManager(size_t elevatorCapacity, size_t passengersIntensity /*= 0*/, 
		int stepsToDo /*= INIFINITY*/, IDrawable* drawer/* = NULL*/) : 
	m_elevator(elevatorCapacity), 
	m_passengersIntensity(passengersIntensity), 
	m_drawer(drawer), 
	m_steps(0), 
	m_transported(0), 
	m_stepsToDo(stepsToDo),
	m_shouldStop(false)
{
}

CFloorsManager::~CFloorsManager(void)
{
	for (FloorsCollection::iterator it = m_floors.begin(); it != m_floors.end(); ++it)
		delete *it;

	if (m_drawer != NULL)
		delete m_drawer;
}

void CFloorsManager::GenerateFloors(size_t num)
{
	m_floors.clear();
	m_floors.reserve(num);
	for (size_t i=0; i<num; ++i)
		m_floors.push_back(new CFloor(i));

	m_elevator.SetFloorsContainer(this);
	m_elevator.SetActionListener(new CStepsChangeListener(*this));
	m_elevator.SetTransportedListener(new CTransportedListener(*this));
	m_elevator.SetIncomingPassengersListener(new CLoadPassengersListener(*this));
}

void CFloorsManager::GeneratePassengers(size_t maxPassengerPerFloor)
{
	for (FloorsCollection::iterator it = m_floors.begin(); it != m_floors.end(); ++it)
	{
		std::vector<size_t> inPassengers;
		CFloor* curFloor = *it;
		size_t pasNum = static_cast<size_t>(rand()) % maxPassengerPerFloor;
		inPassengers.reserve(pasNum);
		for (size_t i=0; i<pasNum; ++i)
		{
			size_t targetNum = static_cast<size_t>(rand()) % m_floors.size();
			if (targetNum == curFloor->GetNum()) // ensure that we won't generate passenger that no needs to go on other floor
			{ 
				if (targetNum == m_floors.size()-1)
					--targetNum;
				else
					++targetNum;
			}
			inPassengers.push_back(targetNum);
		}

		curFloor->AddIncomingPassengers(inPassengers);
	}
}

bool CFloorsManager::ImportPassengersToFloor(size_t floor, const std::vector<size_t>& passengersTargets)
{
	if (floor >= m_floors.size())
		return false;

	m_floors.at(floor)->AddIncomingPassengers(passengersTargets);
	return true;
}

void CFloorsManager::Start()
{
	if (m_passengersIntensity == 0)
		GeneratePassengers(m_floors.size());
	else
		GeneratePassengers(m_passengersIntensity);

	for (size_t i=0; i<m_floors.size(); ++i)
	{
		if (!m_floors[i]->GetIncomingPassengers().empty())
		{
			m_floors[i]->ElevatorLeft();
			break;
		}
	}

	m_shouldStop = false;
	m_elevator.StartMove();
}

void CFloorsManager::SetUpdateListener(IUpdateListener* updateListener)
{
	m_elevator.SetUpdateListener(updateListener);
}

void CFloorsManager::Redraw()
{
	if (m_drawer != NULL)
		m_drawer->Draw(this);
}

size_t CFloorsManager::GetAverageElevatorLoad() const
{
	if (m_incomingPassengersCount.empty())
		return 0;

	size_t res = 0;
	for (size_t i=0; i<m_incomingPassengersCount.size(); ++i)
		res += m_incomingPassengersCount[i];

	return  res / m_incomingPassengersCount.size();
}
/////////////////////////////////////////////////////////////////////////////////////////////////
void CFloorsManager::CStepsChangeListener::OnElevatorAction()
{
	++m_manager.m_steps;
	if (m_manager.m_stepsToDo == INIFINITY)
		return;

	if (m_manager.m_steps == static_cast<size_t>(m_manager.m_stepsToDo))
		m_manager.m_shouldStop = true;
	else if (m_manager.m_steps % 10 == 0)
	{
		for (FloorsCollection::const_iterator it = m_manager.GetFloors().begin(); 
			it != m_manager.GetFloors().end();
			++it)
		{
			CFloor* floor = *it;
			m_manager.GeneratePassengers(m_manager.m_passengersIntensity);
		}
	}
}
/////////////////////////////////////////////////////////////////////////////////////////////////
void CFloorsManager::CTransportedListener::OnPassengerTransported()
{
	++m_manager.m_transported;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
void CFloorsManager::CLoadPassengersListener::LoadPassengersAtArival(PassengerCollection& incomingPassengers)
{
	m_manager.m_incomingPassengersCount.push_back(incomingPassengers.size());
}